﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace InDepth2Demo.Chapter1314
{
    //在接口中使用可变性

    public class InAndOut
    {
        // 1. 用in和out表示可变性
        // 我们使用的两个接口是IEnumerable<T>（对于T是协变的）和IComparer<T>（对于T是逆变的），它们可以很好地展示可变性。以下是它们在.NET 4中的声明：
        // public interface IEnumerable<out T>
        // public interface IComparer<in T>
        public static void Call()
        {
            List<Circle> circles = new List<Circle>
            {
                new Circle(new Point(0, 0), 15),
                new Circle(new Point(10, 5), 20),
            };

            List<Square> squares = new List<Square>
            {
                new Square(new Point(5, 10), 5),
                new Square(new Point(-10, 0), 2)
            };

            //这非常好记：如果类型参数只用于输出，就使用out；如果只用于输入，就用in。

            //协变性  ( AddRange 方法提示的是 IEnumerable<IShape>, circles和squares都将根据协变性而隐式转换为IEnumerable<IShape>)
            List<IShape> shapesByAdding = new List<IShape>();
            shapesByAdding.AddRange(circles);
            shapesByAdding.AddRange(squares);
            List<IShape> shapesByConcat = circles.Concat<IShape>(squares).ToList();

            // 逆变性  （Sort 方法提示的的是 IComparer<Cicle>, 传入的是 IComparer<IShape>，逆变性会进行隐式转换）
            IComparer<IShape> areaComparer = new AreaCompare();
            circles.Sort(areaComparer);
        }
    }

    class AreaCompare : IComparer<IShape>
    {
        public int Compare(IShape x, IShape y)
        {
            return x.Area.CompareTo(y.Area);
        }
    }


    interface IShape
    {
        int Area { get; set; }
    }

    internal class Square : IShape
    {
        public Square(Point point, int length)
        {
            Point = point;
            Length = length;
        }

        public int Area { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }

        Point Point { get; }
        int Length { get; }
    }

    internal class Circle : IShape
    {
        public Circle(Point point, int length)
        {
            Point = point;
            Length = length;
        }

        public int Area { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
        Point Point { get; }
        int Length { get; }
    }

    internal class Point
    {
        private int v1;
        private int v2;

        public Point(int v1, int v2)
        {
            this.v1 = v1;
            this.v2 = v2;
        }
    }
}
